// Relativity Game Engine  *  Gamelab 2009-2010
// Copyright(c) Mark Peters, Quinten Lansu and Bram van de Wetering

#pragma once

#include <GL/glew.h>
#include "vectormath.h"

#include <string>
#include <map>

namespace Tmpl  {

#define VALIDATE_SHADERS

class Shader
{
public:
	Shader();
	virtual ~Shader();

	void LoadShaderFile(const char* a_Filename);

	const char* GetName()const{return &m_Name[0];}
	const char* GetCode()const{return m_Code.c_str();}
	GLuint GetHandle()const{return m_Handle;}
	bool HasFailed()const{return(m_Compiled != GL_TRUE);}

	virtual GLenum GetShaderType() const = 0;
protected:
	void Compile();
	char m_Name[256]; 
	std::string m_Code;
	GLuint m_Handle;
	GLint m_Compiled;
};

class VertexShader : public Shader
{
public:
	virtual GLenum GetShaderType()const{return GL_VERTEX_SHADER;}
};

class FragmentShader : public Shader
{
public:
	virtual GLenum GetShaderType()const{return GL_FRAGMENT_SHADER;}
};

class matrix;
typedef std::map<std::string, GLint> LocMap;
class ShaderProgram
{
public:
	ShaderProgram();
	~ShaderProgram();

	void LoadShader(const char* a_VertexShader, const char* a_FragmentShader);

	// uniforms
	GLint GetUniformLoc(const char* a_Name);
	void Uniform1i(const char* a_Name, GLint a_Val);
	void Uniform1f(const char* a_Name, float a_Val);
	void Uniform2f(const char* a_Name, float a_Val1, float a_Val2);
	void Uniform3f(const char* a_Name, Vec3f a_Val);
	void Uniform3f(const char* a_Name, float a_Val1, float a_Val2, float a_Val3);
	void Uniform4f(const char* a_Name, float a_Val1, float a_Val2, float a_Val3, float a_Val4);
	void UniformMatrix4x4(const char* a_Name, const float* a_Matrix);
	void UniformMatrix2x2(const char* a_Name, const float* a_Matrix);

	// uniform block
	GLuint GetUniformBlockIndex(const char* a_Name);
	void GetActiveUniformBlockiv(GLuint uniformBlockIndex, GLenum pname, GLint* params);
	void GetUniformIndices(GLsizei uniformCount, const char** uniformNames, GLuint* uniformIndices);
	void GetActiveUniformsiv(GLsizei uniformCount, const GLuint* uniformIndices, GLenum pname, GLint* params);
	void UniformBlockBinding(GLuint uniformBlockIndex, GLuint uniformBlockBinding);

	// attribs
	GLint GetAttribLoc(const char* a_Name);

	// binding
	void Use();
	void UnUse();

	// Handle
	GLuint GetHandle()const{return m_Handle;}
	bool IsFailed() const { return (m_Linked != GL_TRUE); }
private:
	GLuint m_Handle;
	GLint m_Linked;
	VertexShader*	m_VertexShader;
	FragmentShader*	m_FragmentShader;
	LocMap m_UniformLocMap;
	LocMap m_AttribLocMap;
};

class ShaderManager
{
public:
	static ShaderProgram* LoadShader(const char* a_FileName);
	static void UnUse(){ glUseProgram(0); }
	static void Reload();
	static void Clear() { m_Shaders.clear(); }
private:
	typedef std::map<std::string, ShaderProgram*> ShaderMap;
	static std::map<std::string, ShaderProgram*> m_Shaders;
};

}; // namespace relativity
